﻿using System;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class CFVO
    {
        private CFVOType cfvoType;
        private CFVOParsedFormula fmla;
        private double? numValue;

        public CFVO(ConditionalFormatValueObject conditionalFormatValueObject)
        {
            Contract.Requires(conditionalFormatValueObject != null);
            cfvoType = ToBiff(conditionalFormatValueObject.Type);
            fmla = new CFVOParsedFormula(); //\\
            if (fmla.TokenCount == 0 && cfvoType != CFVOType.Minimum && cfvoType != CFVOType.Maximum)
            {
                double d;
                if (double.TryParse(conditionalFormatValueObject.Value, out d))
                    numValue = d;
                else
                {
                    numValue = 0;
                }
            }
        }

        public CFVO()
        {
        }

        private static CFVOType ToBiff(ConditionalFormatValueObjectType value)
        {
            Contract.Ensures(FromBiff(Contract.Result<CFVOType>()) == value);
            switch (value)
            {
                case ConditionalFormatValueObjectType.Formula:
                    return CFVOType.FormulaResult;
                case ConditionalFormatValueObjectType.Maximum:
                    return CFVOType.Maximum;
                case ConditionalFormatValueObjectType.Minimum:
                    return CFVOType.Minimum;
                case ConditionalFormatValueObjectType.Number:
                    return CFVOType.X;
                case ConditionalFormatValueObjectType.Percent:
                    return CFVOType.MinimumPlusXPercent;
                case ConditionalFormatValueObjectType.Percentile:
                    return CFVOType.MinimumInXPercentile;
                default:
                    throw new NotSupportedException();
            }
        }

        public void Read(BinaryReader reader)
        {
            cfvoType = (CFVOType) reader.ReadByte();
            fmla = new CFVOParsedFormula();
            fmla.Read(reader);
            if (fmla.TokenCount == 0 && cfvoType != CFVOType.Minimum && cfvoType != CFVOType.Maximum)
                numValue = reader.ReadDouble();
        }

        public void Write(BinaryWriter writer)
        {
            writer.Write((byte) cfvoType);
            fmla.Write(writer);
            if (numValue != null)
                writer.Write(numValue.Value);
        }

        public ConditionalFormatValueObject ToCondition(BiffReaderWorksheetContext context)
        {
            string value;
            if (numValue != null)
                value = numValue.Value.ToString();
            else
            {
                var exp = fmla.GetExpression(context.ParentContext);
                if (exp != null)
                    value = exp.GetText(new TextContext(new WorksheetEvaluationContext(context.Worksheet)));
                else
                {
                    value = null;
                }
            }
            var result = new ConditionalFormatValueObject(FromBiff(cfvoType), value);
            return result;
        }

        private static ConditionalFormatValueObjectType FromBiff(CFVOType value)
        {
            switch (value)
            {
                case CFVOType.FormulaResult:
                    return ConditionalFormatValueObjectType.Formula;
                case CFVOType.Maximum:
                    return ConditionalFormatValueObjectType.Maximum;
                case CFVOType.Minimum:
                    return ConditionalFormatValueObjectType.Minimum;
                case CFVOType.MinimumInXPercentile:
                    return ConditionalFormatValueObjectType.Percentile;
                case CFVOType.MinimumPlusXPercent:
                    return ConditionalFormatValueObjectType.Percent;
                case CFVOType.X:
                    return ConditionalFormatValueObjectType.Number;
                default:
                    throw new NotSupportedException();
            }
        }
    }
}